package ext.render;

import context.render.vertexdata.VertexData;
import math.mat3;
import static math.math.*;
import static math.vec2.*;
import math.vec2;
import static math.vec3.*;
import math.vec3;

public class RenderSphere
{
    
    private RenderSphere()
    {
    }
    
    private static final int NUM_SUBDIVISION_STEPS = 1;
    
    
    public static void render(
            VertexData aVertexData, 
            vec3 aX, 
            mat3 aR, 
            double aRadius)
    {
        Tri[] ts = getTriSetCopy();
        for (Tri t : ts)
        {
            t.v0.mul(aRadius);
            t.v1.mul(aRadius);
            t.v2.mul(aRadius);
            
            t.v0.set(aR.mul(t.v0));
            t.v1.set(aR.mul(t.v1));
            t.v2.set(aR.mul(t.v2));
            
            t.n0.set(aR.mul(t.n0)); t.n0.norm();
            t.n1.set(aR.mul(t.n1)); t.n1.norm();
            t.n2.set(aR.mul(t.n2)); t.n2.norm();
            
            t.v0.add(aX);
            t.v1.add(aX);
            t.v2.add(aX);
            
            renderTri(aVertexData, t);
        }
    }
    
    private static void renderTri(VertexData vd, Tri t)
    {
        vd.addVert(new double[] { t.v0.x, t.v0.y, t.v0.z,   t.n0.x, t.n0.y, t.n0.z,   t.u0.x, t.u0.y });
        vd.addVert(new double[] { t.v1.x, t.v1.y, t.v1.z,   t.n1.x, t.n1.y, t.n1.z,   t.u1.x, t.u1.y });
        vd.addVert(new double[] { t.v2.x, t.v2.y, t.v2.z,   t.n2.x, t.n2.y, t.n2.z,   t.u2.x, t.u2.y });
    }
    
    
    private static class Tri
    {
        
        public vec3 v0, v1, v2;
        public vec3 n0, n1, n2;
        public vec2 u0, u1, u2;
        
        public Tri(vec3 v0, vec3 v1, vec3 v2)
        {
            this(v0, v1, v2,  v0, v1, v2,  RNG.rand2f(), RNG.rand2f(), RNG.rand2f());
        }
        
        public Tri(
                vec3 v0, vec3 v1, vec3 v2, 
                vec3 n0, vec3 n1, vec3 n2, 
                vec2 u0, vec2 u1, vec2 u2)
        {
            this.v0 = v0;
            this.v1 = v1;
            this.v2 = v2;
            
            this.n0 = n0;
            this.n1 = n1;
            this.n2 = n2;
            
            this.u0 = u0;
            this.u1 = u1;
            this.u2 = u2;
        }
        
        public Tri copy()
        {
            return new Tri(
                v0.copy(), v1.copy(), v2.copy(), 
                n0.copy(), n1.copy(), n2.copy(), 
                u0.copy(), u1.copy(), u2.copy()
            );
        }
        
    }
    
    private static Tri[] TRI_SET; // Unit sphere set of triangles.
    
    static // Generate and subdivide the rendering sphere.
    {
        TRI_SET = new Tri[]
        {
            new Tri(vec3.AXIS_XN, vec3.AXIS_YN, vec3.AXIS_ZN), 
            new Tri(vec3.AXIS_XN, vec3.AXIS_ZP, vec3.AXIS_YN), 
            
            new Tri(vec3.AXIS_XN, vec3.AXIS_ZN, vec3.AXIS_YP), 
            new Tri(vec3.AXIS_XN, vec3.AXIS_YP, vec3.AXIS_ZP), 
            
            new Tri(vec3.AXIS_XP, vec3.AXIS_ZN, vec3.AXIS_YN), 
            new Tri(vec3.AXIS_XP, vec3.AXIS_YN, vec3.AXIS_ZP), 
            
            new Tri(vec3.AXIS_XP, vec3.AXIS_YP, vec3.AXIS_ZN), 
            new Tri(vec3.AXIS_XP, vec3.AXIS_ZP, vec3.AXIS_YP)
        };
        
        for (int i = 0; i < NUM_SUBDIVISION_STEPS; i++)
        {
            int N = TRI_SET.length;
            Tri[] next = new Tri[4 * N];
            int j = 0;
            for (Tri t : TRI_SET)
            {
                vec3 mid01v = add(t.v0, t.v1);
                mid01v.norm();
                vec2 mid01u = add(t.u0, t.u1);
                mid01u.mul(0.5);
                
                vec3 mid12v = add(t.v1, t.v2);
                mid12v.norm();
                vec2 mid12u = add(t.u1, t.u2);
                mid12u.mul(0.5);
                
                vec3 mid20v = add(t.v2, t.v0);
                mid20v.norm();
                vec2 mid20u = add(t.u2, t.u0);
                mid20u.mul(0.5);
                
                Tri t0 = new Tri(t.v0, mid01v, mid20v);
                Tri t1 = new Tri(mid20v, mid12v, t.v2);
                Tri t2 = new Tri(mid01v, t.v1, mid12v);
                Tri t3 = new Tri(mid20v, mid01v, mid12v);
                
                next[j++] = t0;
                next[j++] = t1;
                next[j++] = t2;
                next[j++] = t3;
            }
            
            TRI_SET = next;
        }
    }
    
    
    private static Tri[] getTriSetCopy()
    {
        Tri[] copy = new Tri[TRI_SET.length];
        for (int i = 0; i < TRI_SET.length; i++)
        {
            copy[i] = TRI_SET[i].copy();
        }
        return copy;
    }
    
}
